#include "ukuiwindowmanager.h"
#include "ukui-wayland-client.h"
#include "ukuiwindow.h"
#include "wayland-ukui-window-management-client-protocol.h"

#include <QDebug>

#include <fcntl.h>
#include <qdir.h>
#include <qfileinfo.h>
#include <qthread.h>
#include <sys/poll.h>
#include <sys/stat.h>

Q_LOGGING_CATEGORY(UKUIWAYLANDPROP, "ukui.wayland.prop", QtDebugMsg)

namespace ukuiwaylandclient
{

UkuiWindowManager::UkuiWindowManager()
{
}

UkuiWindowManager::~UkuiWindowManager()
{
    disconnectFromWayland();

    m_stop = true;

    wait();
}

UkuiWindow *UkuiWindowManager::findWindowOnPosition(const QPoint &position) const
{
    int stackingOrderSize = m_stackingOrderWindows.size();
    if (stackingOrderSize > 0) {
        for (int i = 0; i < stackingOrderSize; ++i) {
            UkuiWindow *window = m_stackingOrderWindows.at(i);
            if (window && window->geometry().contains(position) && window->appId() != "ukuiwaylandprop") {
                return window;
            }
        }
    }

    int size = m_windows.size();
    for (int i = 0; i < size; ++i) {
        UkuiWindow *window = m_windows.at(i);
        if (window && window->geometry().contains(position) && window->appId() != "ukuiwaylandprop") {
            return window;
        }
    }

    return nullptr;
}

void UkuiWindowManager::run()
{
    while (!m_stop) {
        if (!m_display) {
            if (!connectToWayland()) {
                QThread::msleep(1000); // Retry after a delay
                continue;
            }
        }

        int fd = m_display->get_fd();
        pollfd pfd = {fd, POLLIN, 0};

        if (m_display->prepare_read() != 0) {
            m_display->dispatch_pending();
            continue;
        }

        m_display->flush();

        int ret = poll(&pfd, 1, -1);
        if (ret < 0) {
            m_display->cancel_read();
            continue;
        }

        if (pfd.revents & POLLIN) {
            if (m_display->read_events() == -1) {
                disconnectFromWayland();
                continue;
            }
            m_display->dispatch_pending();
        } else {
            m_display->cancel_read();
        }
    }
}

bool UkuiWindowManager::connectToWayland()
{
    m_display = new wl_display_t();
    if (!m_display) {
        qWarning(UKUIWAYLANDPROP) << "Failed to connect to Wayland display!";
        return false;
    }

    m_registry = m_display->get_registry();
    if (!m_registry) {
        qWarning(UKUIWAYLANDPROP) << "Failed to get Wayland registry!";
        delete m_display;
        m_display = nullptr;
        return false;
    }

    m_registry->on_global() = [this](uint32_t name, const char *interface, uint32_t version) {
        if (strcmp(interface, ukui_window_management_interface.name) == 0) {
            m_ukuiWindowManagement = new ukui_window_management_t(m_registry, name, std::min<uint32_t>(version, ukui_window_management_interface.version));
        }
    };

    m_display->roundtrip(); // Ensure the registry is fully populated

    if (m_ukuiWindowManagement) {
        m_ukuiWindowManagement->on_window_created() = [this](const char *uuid) {
            ukui_window_t *ukui_window = m_ukuiWindowManagement->create_window(uuid);
            int id = wl_proxy_get_id((struct ::wl_proxy *)(ukui_window->object()));
            UkuiWindow *window = new UkuiWindow(uuid, id);
            ukui_window->on_app_id_changed() = [window](const char *appId) {
                window->setAppId(QString::fromUtf8(appId));
            };
            ukui_window->on_title_changed() = [window](const char *title) {
                window->setTitle(QString::fromUtf8(title));
            };
            ukui_window->on_pid_changed() = [window](int64_t pid) {
                window->setPid(pid);
            };
            ukui_window->on_geometry() = [window](uint x, uint y, uint width, uint height) {
                window->setGeometry(QRect(x, y, width, height));
            };
            ukui_window->on_state_changed() = [window](uint32_t state) {
                window->setState(state);
            };
            ukui_window->on_initial_state() = [this, uuid]() {
                Q_EMIT finished(QString::fromUtf8(uuid));
            };
            ukui_window->on_themed_icon_name_changed() = [window](const char *name) {
                window->setThemedIconName(QString::fromUtf8(name));
            };
            ukui_window->on_unmapped() = [this, window]() {
                Q_EMIT windowRemoved(window);
                m_windows.removeAll(window);
                delete window;
            };
            ukui_window->on_resource_name_changed() = [window](const char *resource_name) {
                window->setResourceName(QString::fromUtf8(resource_name));
            };
            ukui_window->on_parent_window() = [this, window, ukui_window](struct ::ukui_window *parent) {
                int parentObjectId = wl_proxy_get_id((struct ::wl_proxy *)(parent));
                for (UkuiWindow *w : m_windows) {
                    if (w->objectId() == parentObjectId) {
                        window->setParentWindow(w);
                        break;
                    }
                }
            };
            ukui_window->on_virtual_desktop_entered() = [window](const char *virtualDesktopId) {
                window->setVirtualDesktop(QString::fromUtf8(virtualDesktopId));
            };

            Q_EMIT windowAdded(window);
            m_windows.append(window);
        };

        m_ukuiWindowManagement->on_stacking_order_changed() = [this](const char *uuids) {
            QStringList uuidList = QString::fromUtf8(uuids).split(';');
            m_stackingOrderWindows.clear();
            for (const QString &uuid : uuidList) {
                auto it = std::find_if(m_windows.begin(), m_windows.end(), [&uuid](UkuiWindow *window) {
                    return window->uuid() == uuid;
                });
                if (it != m_windows.end()) {
                    m_stackingOrderWindows.append(*it);
                }
            }
        };
    }

    return true;
}

void UkuiWindowManager::disconnectFromWayland()
{
    if (m_ukuiWindowManagement) {
        delete m_ukuiWindowManagement;
        m_ukuiWindowManagement = nullptr;
    }

    if (m_registry) {
        delete m_registry;
        m_registry = nullptr;
    }

    if (m_display) {
        delete m_display;
        m_display = nullptr;
    }
}

} // namespace ukuiwaylandclient
